package com.logicfishsoftware.wolfgo.meta.impl;

import java.util.Collection;
import java.util.Collections;

import org.apache.commons.io.FilenameUtils;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.object.ObjectConnection;
import org.openrdf.repository.object.ObjectQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.logicfishsoftware.wolfgo.api.VirtualContentConnection;
import com.logicfishsoftware.wolfgo.api.VirtualDirectory;
import com.logicfishsoftware.wolfgo.api.VirtualFile;
import com.logicfishsoftware.wolfgo.api.VirtualFileSystemConnection;
import com.logicfishsoftware.wolfgo.api.VirtualFileSystemConstants;
import com.logicfishsoftware.wolfgo.api.VirtualFileSystemException;
import com.logicfishsoftware.wolfgo.meta.SemanticFileSystemProvider.SemanticFileSystemHandler;

class SemanticFileSystemConnection implements VirtualFileSystemConnection{

	final static Logger log = LoggerFactory.getLogger(SemanticFileSystemConnection.class);
	
	private ObjectConnection connection;
	private String baseURI = "http://logicfishsoftware.com/wolfgo/fsys";

	private SemanticFileSystemHandler handler;

	public SemanticFileSystemConnection(ObjectConnection connection,SemanticFileSystemHandler handler) {
		this.connection = connection;
		this.handler = handler;
	}

	private String getFileURI(String fileName) {
		fileName = FilenameUtils.separatorsToUnix(fileName);
		if(fileName==null) return baseURI + "/";
		if(fileName.endsWith("/")) fileName = fileName.substring(0,fileName.length()-1);
		if("".equals(fileName)) return baseURI + "/";
		return baseURI + fileName;
	}
	private VirtualFile getFileSubject(String fileName) throws SemanticFileSystemException {
		log.info("File lookup: " + fileName);
		String uri = getFileURI(fileName);
		try {
			VirtualFile object = connection.getObject(VirtualFile.class,uri);
			return object;
		} catch (RepositoryException | QueryEvaluationException e) {
			throw new SemanticFileSystemException(e);
		} catch (ClassCastException e) {
			return null;
		}
	}
	private VirtualDirectory getDirectorySubject(String fileName) throws SemanticFileSystemException {
		log.info("Dir lookup: " + fileName);
		String uri = getFileURI(fileName);
		try {
			VirtualDirectory object = connection.getObject(VirtualDirectory.class,uri);
			return object;
		} catch (ClassCastException e) {
			return null;
		} catch (RepositoryException | QueryEvaluationException e) {
			throw new SemanticFileSystemException(e);
		}
	}


	private void putSubject(String fileName, VirtualFile file) throws SemanticFileSystemException {
		String uri = getFileURI(fileName);
		log.info("put subject: " + uri);
		try {
//			VirtualFile fileSubject = connection.getObjectFactory().createObject(uri,VirtualFile.class);
			file.setFileName(fileName);
			file.setParent(locateParent(file));
			connection.addObject(uri,file);
//			connection.addDesignation(connection.getObject(uri), VirtualFile.class);
			//		connection.getObject(VirtualFile.class,uri).setParent(locateParent(fileSubject));
		} catch (RepositoryException e) {
			throw new SemanticFileSystemException(e);
		}
	}
	
	private VirtualDirectory locateParent(VirtualFile file) throws SemanticFileSystemException {
		log.info("Find parent " + file.getFileName());
		if("/".equals(file.getFileName())) {
			return null;
		}
		if(isDirectory(file)) {
			String path = FilenameUtils.getPath(file.getFileName());
			int sep = FilenameUtils.indexOfLastSeparator(path);
			if(sep==-1) return getDirectorySubject("/");
			return getDirectorySubject(path.substring(0,sep));
		} else {
			return getDirectorySubject("/" + FilenameUtils.getPath(file.getFileName()));
		}
		
	}

	private VirtualFile removeFileSubject(String fileName) throws SemanticFileSystemException {
		log.info("File remove: " + fileName);
		String uri = getFileURI(fileName);
		try {
			VirtualFile fileSubject = connection.getObject(VirtualFile.class,uri);
			connection.removeDesignation(fileSubject, VirtualFile.class);
			connection.removeDesignation(fileSubject, VirtualDirectory.class);
			return fileSubject;
		} catch (RepositoryException | QueryEvaluationException e) {
			throw new SemanticFileSystemException(e);
		}		
	}
	private boolean isDirectory(VirtualFile fi) {
		return fi instanceof VirtualDirectory;
	}

	

	@Override
	public boolean exists(String fileName) throws SemanticFileSystemException {
		log.info("Exists: " + fileName);
		VirtualFile fileSubject = getFileSubject(fileName);
		return fileSubject != null;
	}


//	@Override
//	public void put(String fileName, VirtualFile fi) throws SemanticFileSystemException {
//		log.info("Put file: " + fileName);
//		putSubject(FilenameUtils.separatorsToUnix(fileName), fi);
//	}
	
	@Override
	public <T extends VirtualFile> T put(String fileName, Class<T> cls)
			throws VirtualFileSystemException {
		String uri = getFileURI(fileName);
		log.info("Put: " + uri);
		T t = connection.getObjectFactory().createObject(uri,cls);
		putSubject(FilenameUtils.separatorsToUnix(fileName),t);
		return t;
	}



	@Override
	public boolean dirExists(String pathName) throws SemanticFileSystemException {
		log.info("Dir exists: " + pathName);
		VirtualDirectory directorySubject = getDirectorySubject(pathName);
		return directorySubject!=null;
	}

	@Override
	public VirtualFile get(String fileName) throws SemanticFileSystemException {
		log.info("Get: " + fileName);
		return getFileSubject(fileName);
	}

	@Override
	public Collection<VirtualFile> getChildren(String pathName) throws SemanticFileSystemException {
		log.info("Get children: " + pathName);
		VirtualDirectory directory = getDirectorySubject(pathName);
		if(directory!=null) {
			return directory.getChildren();
//			try {
//				ObjectQuery query = connection.prepareObjectQuery(
//						"PREFIX wgo:<"+VirtualFileSystemConstants.NS+"#>\n"+
//						"SELECT ?file WHERE {?file wgo:fileParent $file}");
//				query.setObject("file", directory);
//				return query.evaluate(VirtualFile.class).asSet();
//			} catch (MalformedQueryException | RepositoryException | QueryEvaluationException e) {
//				throw new SemanticFileSystemException(e);
//			}
		}
		return Collections.emptySet();
	}

	@Override
	public void setAttributes(String fileName, int fileAttributes) throws SemanticFileSystemException {
		log.info("Set attr: " + fileName);
		VirtualFile fileSubject = getFileSubject(fileName);
		fileSubject.setFileAttribute(fileAttributes);
	}

	@Override
	public VirtualFile delete(String fileName) throws SemanticFileSystemException {
		log.info("Delete: " + fileName);
		return removeFileSubject(fileName);
	}


	@Override
	public VirtualContentConnection getContentConnection() {
		log.info("Get content connection.");
		return new SemanticContentConnection(connection);
	}

	@Override
	public void close()  {
		try {
			handler.disposeConnection(connection);
		} catch (RepositoryException e) {
			log.warn("Error closing object repository connection:",e);
		}
		log.info("Closed.");
	}

	@Override
	public VirtualDirectory getDir(String fileName)
			throws VirtualFileSystemException {
		log.info("Get dir: "+fileName);
		return getDirectorySubject(fileName);
	}

	@Override
	public void rollback(VirtualFileSystemException e) {
		handler.rollback(e,connection);
	}
	
}